#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""An extensible CSS parser written in Python using pyparsing.
"""

import pyparsing
import re

from pyparsing import ZeroOrMore, OneOrMore, Group, Combine, Word, Suppress
from pyparsing import Optional, restOfLine, Regex, White
from pyparsing import nums, alphanums, restOfLine, pythonStyleComment, Literal


# from http://www.w3.org/TR/CSS2/syndata.html
macros = {
    'ident': r'[-]?{nmstart}{nmchar}*',
    'name':	r'{nmchar}+',
    'nmstart': r'[_a-z]|{nonascii}|{escape}',
    'nonascii': r'[^\0-\177]',
    'unicode': r'\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?',
    'escape': r'{unicode}|\\[^\n\r\f0-9a-f]',
    'nmchar': r'[_a-z0-9-]|{nonascii}|{escape}',
    'num': r'[0-9]+|[0-9]*\.[0-9]+',
    'string': r'{string1}|{string2}',
    'string1': r'\"([^\n\r\f\\\"]|\\{nl}|{escape})*\"',
#   'string1': r'"([^\n\r\f"]|\\{nl}|{escape})*"',
    'string2': r'\'([^\n\r\f\\\']|\\{nl}|{escape})*\'',
    'invalid': r'{invalid1}|{invalid2}',
    'invalid1':	r'\"([^\n\r\f\\\"]|\\{nl}|{escape})*',
    'invalid2':	r'\'([^\n\r\f\\\']|\\{nl}|{escape})*',
    'nl': r'\n|\r\n|\r|\f',
    'w': r'[ \t\r\n\f]*',
}

m_unicode = (
    Literal('\\').suppress() +
    Regex('[0-9a-fA-F]{1,6}') +
    Optional(White(exact=1)).suppress()
)

m_escape = m_unicode | Literal('\\').suppress() + Regex(u'[^\n\r\f0-9a-f]')
m_nonascii = Regex(u'[^\x00-\x7f]')
m_nmchar = Regex(u'[0-9A-Za-z-]') | m_nonascii | m_escape
m_name = OneOrMore(m_nmchar)
m_nmstart = Regex(u'[a-zA-Z_]') | m_nonascii | m_escape
m_nonascii = Regex(u'[^\x00-\x7f]')
m_identifier = m_nmstart + ZeroOrMore(m_nmchar)

tokens = {
    'IDENT': r'{ident}',
    'ATKEYWORD': r'@{ident}',
    'STRING':	r'{string}',
    'INVALID':	r'{invalid}',
    'HASH':	r'#{name}',
    'NUMBER': r'{num}',
    'PERCENTAGE': r'{num}%',
    'DIMENSION': r'{num}{ident}',
    'URI': r'url\({w}{string}{w}\)|url\({w}([!#$%&*-~]|{nonascii}|{escape})*{w}\)',
    'UNICODE-RANGE': r'u\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})?',
    'CDO': r'<!--',
    'CDC': r'-->',
    ':': r':',
    ';': r';',
    '{': r'\{',
    '}': r'\}',
    '(': r'\(',
    ')': r'\)',
    '[': r'\[',
    ']': r'\]',
    'S': r'[ \t\r\n\f]+',
    'COMMENT': r'\/\*[^*]*\*+([^/*][^*]*\*+)*\/',
    'FUNCTION': r'{ident}\(',
    'INCLUDES': r'~=',
    'DASHMATCH': r'|=',
    'DELIM': r'any other character not matched by the above rules, and neither a single nor a double quote',
}

COLON  = Suppress(':')
SEMICOLON = Suppress(';')
LACC = Suppress('{')
RACC = Suppress('}')
LPAREN = Suppress('(')
RPAREN = Suppress(')')
LBRACK = Suppress('[')
RBRACK = Suppress(']')
OR = Suppress('|')

core = {
    'stylesheet': '[ CDO | CDC | S | statement ]*;',
    'statement': 'ruleset | at-rule;',
    'at-rule': 'ATKEYWORD S* any* [ block | ; S* ];',
    'block': '{ S* [ any | block | ATKEYWORD S* | ; S* ]* } S*;',
    'ruleset': 'selector? { S* declaration? [ ; S* declaration? ]* } S*;',
    'selector': 'any+;',
    'declaration': 'property S* : S* value;',
    'property': 'IDENT;',
    'value': '[ any | block | ATKEYWORD S* ]+;',
    'any': '[ IDENT | NUMBER | PERCENTAGE | DIMENSION | STRING | DELIM | ' +
            'URI | HASH | UNICODE-RANGE | INCLUDES | DASHMATCH | : | ' +
            'FUNCTION S* any* ) | ( S* any* ) | [ S* any* ] ] S*;',
}

# Stylesheet
# Statement
# AtRule
# FontFaceRule: @font-face {}
# Identifier: font-face
# ImportRule: @import url("style.css");
# MediaRule: @media screen {}
# NameSpaceRule: @namespace xhtml "http://www.w3.org/1999/xhtml";
# PageRule: @page {}
# CharsetRule: @charset "utf-8";
# Rule: div, p > a { display: block; border: 1px solid red; }
# SelectorGroup: div, p > a
# Selector: p > a
# SimpleSelector: p
# Combinator: >
# DeclarationBloc: { display: block; border: 1px solid red; }
# Declaration: border: 1px solid red;
# Property: border
# ValueList: `1px solid red'
# Value: '1px`


CDO = r'<!--'
CDC = r'-->'

class Stylesheet(object):

    parser = (
    )

    def __init__(self):
        pass


class Statement(object):

    parser = (
    )

    def __init__(self):
        pass


class AtRule(object):

    parser = (
    )

    def __init__(self):
        pass


class FontFaceRule(object):

    parser = (
    )

    def __init__(self):
        pass


class Identifier(object):

    parser = (
    )

    def __init__(self):
        pass


class ImportRule(object):

    parser = (
    )

    def __init__(self):
        pass


class MediaRule(object):

    parser = (
    )

    def __init__(self):
        pass


class NameSpaceRule(object):

    parser = (
    )

    def __init__(self):
        pass


class PageRule(object):

    parser = (
    )

    def __init__(self):
        pass


class CharsetRule(object):

    parser = (
    )

    def __init__(self):
        pass


class Rule(object):

    parser = (
    )

    def __init__(self):
        pass


class Selector(object):

    parser = (
        OneOrMore(
            Word(alphanums + ':')
        )
    )

    def __init__(self):
        pass


class SelectorGroup(object):

    parser = (
        Group(
            Selector.parser +
            ZeroOrMore(
                Word(',').suppress() +
                Selector.parser
            )
        ).setParseAction(
            lambda s, l, t: SelectorGroup(*t[0])
        )
    )

    def __init__(self, *selectors):
        self.selectors = selectors

    def __getitem__(self, index):
        return self.selectors[index]


class DeclarationBloc(object):

    parser = (
    )

    def __init__(self):
        pass


class Declaration(object):

    parser = (
    )

    def __init__(self):
        pass


class Property(object):

    parser = (
    )

    def __init__(self):
        pass


class ValueList (object):

    parser = (
    )

    def __init__(self):
        pass


class Value (object):

    parser = (
    )

    def __init__(self):
        pass


parser = (
    OneOrMore(
        SelectorGroup.parser +
        Word('{').suppress() +
        ZeroOrMore(
            Word(alphanums + '-') +
            Word(':').suppress() +
            OneOrMore(
                Word(alphanums + '-."\\' )
            ) +
            Optional(';').suppress()
        ) +
        Word('}').suppress()
    )
)
